<?php
namespace app\first\controller;

use app\admin\model\File;
use app\admin\model\FileType;
use app\admin\model\Pic;
use app\admin\model\PicType;
use Curl\Curl;
use OSS\Core\OssException;
use OSS\OssClient;
use OSS\Result\UploadPartResult;
use think\Exception;
use think\facade\Env;
use think\Image;

/**
 * @title 阿里云上传
 * @url /v1/aliyun
 * @desc  阿里云上传相关接口
 * @version 1.0
 * @route('v1/aliyun')
 * Class AliyunUpload
 * @package app\first\controller
 */
class AliyunUpload extends Base {
    public $apiAuth = false;
    private $client = null;
    private $appid;
    private $secret;
    private $endpoint;
    private $file_type;
    private $bucketName;
    private $picurl;
    private $ext ;
    private $type_id;
    private $template_id;
    private $finder;

    public function __construct($type_id = 4,$file_type='pic'){
        parent::__construct();
        $this->appid = Env::get('ALIYUN.ALIYUN_ACCESS_KEY_ID');
        $this->secret = Env::get('ALIYUN.ALIYUN_ACCESS_KEY_SECRET');
        $this->endpoint = Env::get('ALIYUN.ALIYUN_ENDPOINT');
        $this->client = new OssClient($this->appid,$this->secret,$this->endpoint);
        $this->bucketName = $this->picurl = $this->finder = $this->ext = $this->type_id = $this->template_id = '';
        $this->file_type = $file_type;
        if($file_type == 'pic'){
            //获取pic_type对应的参数
            $PicType = new PicType();
            $type_detail = $PicType->getById($type_id);
            if(empty($type_detail)){
                return false;
            }
            $this->bucketName = $type_detail['bucket_name'];
            $this->picurl = $type_detail['picurl']; //图片域名
            $this->finder = $type_detail['finder_name']; //图片的文件夹名称
            $this->ext = $type_detail['ext']; //图片后缀
            $this->type_id = $type_id;
        }else{
            //上传文件待完善
            $FileType = new FileType();
            $type_detail = $FileType->getById($type_id);
            $this->finder = $type_detail['finder_name']; //图片的文件夹名称
            $this->ext = $type_detail['ext']; //图片后缀
            $this->template_id = $type_detail['template_id'];
            $this->type_id = $type_id;
            $this->picurl = $type_detail['picurl'];
        }
    }

    /**
     * @title 下载文件
     * @method download
     * @param string $object 下载对象 true resource_water/dav-resource_water19090409464667109.jpg
     * @param int $range 分片下载大小 false 1024 0全部下载,其他每次下载字节数
     * @route('v1/aliyun/download','get')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\View|\think\response\Xml
     * @throws \ErrorException
     */
    public function download($object='',$range=0){
        $option = null;
        $isExist = $this->client->doesObjectExist($this->bucketName,$object);
        if(!$isExist){
            return $this->sendError(0,'下载文件不存在');
        }
        $url = "https://jiexun.oss-cn-hangzhou.aliyuncs.com/{$object}?x-oss-process=image/info";
        $curl = new Curl();
        $response = ($curl->get($url))->response;
        $size = \GuzzleHttp\json_decode($response,true)['FileSize']['value'];
        $curl->close();
        if($range){
            $part = $this->client->generateMultiuploadParts($size, $range);
            header("Content-type:application/octet-stream");
            header("Accept-Ranges:bytes");
            header("Content-Disposition: attachment; filename=".$object);
            foreach ($part as $k => $v) {
                $len = $v['seekTo'] + $v['length'];
                header("Accept-Length:".$len);
                $options = array(OssClient::OSS_RANGE => "{$v['seekTo']}-{$v['length']}");
                echo $this->client->getObject($this->bucketName,$object,$options);
            }
            exit();
        }else {
            $data = $this->client->getObject($this->bucketName,$object);
            header("Content-type:application/octet-stream");
            header("Accept-Ranges:bytes");
            header("Accept-Length:".$size);
            header("Content-Disposition: attachment; filename=".$object);
            echo $data;
            exit();
        }
    }

    /**
     * @title 上传图片
     * @method upload_image
     * @param string $file 文件名 true image
     * @param int $type 上传类型 true 4 1用户头像,2Banner,3资源图片无水印,4资源图片有水印
     * @route('v1/aliyun/upload_image','post')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\View|\think\response\Xml
     */
    public function upload_image($file='image',$type=4){
        if($type){
            $type_detail = (new PicType())->getById($type);
            $this->bucketName = $type_detail['bucket_name'];
            $this->picurl = $type_detail['picurl']; //图片域名
            $this->finder = $type_detail['finder_name']; //图片的文件夹名称
            $this->ext = $type_detail['ext']; //图片后缀
            $this->type_id = $type;
        }
        try {
            $_file = request()->file($file);
            $info = $_file->getInfo();
            $image = Image::open($info['tmp_name']);
            $w = $image->width();
            $h = $image->height();
            $path = $this->setPicName($name);
            if(empty($this->ext)){
                $_name = explode('.',$info['tmp_name']);
                $this->ext = end($_name);
            }
            $fileName = $path.$this->ext;
            $result = $this->client->uploadFile($this->bucketName,$fileName,$info['tmp_name']);
            $url = $this->picurl.parse_url($result['info']['url'])['path'];
            $_path = "{$url}@!{$this->finder}";
            $pic = new Pic();
            $id = $pic->addNew($name.$this->ext,$_path,$this->type_id,$w,$h,$info['size'],self::$uid);
            $out = [
                '_id'=>$id,
                '_path'=>$_path
            ];
            return $this->sendSuccess($out);
        } catch (OssException $e) {
          return $this->sendError(['status'=>1,'path'=>$e->getMessage()]);
        }
    }

    /**
     * @title 上传文件
     * @method upload_file
     * @param string $file 上传名 true file
     * @param int $type 上传类型 true 1 1视频文件,2音频文件,3ppt文件,4pdf文件,5pptx文件,6所有文件
     * @route('v1/aliyun/upload_file','post')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\View|\think\response\Xml
     */
    public function upload_file($file='file',$type=1){
        if($type){
            $type_detail = (new FileType())->getById($type);
            $this->finder = $type_detail['finder_name'];
            $this->ext = $type_detail['ext'];
            $this->type_id = $type;
        }
        try {
            $_file = request()->file($file);
            $info = $_file->getInfo();
            $path = $this->setPicName($name);
            if(empty($this->ext)){
                $_name = explode('.',$info['name']);
                $this->ext = '.'.end($_name);
            }
            $fileName = $path.$this->ext;
            $result = $this->client->uploadFile($this->bucketName,$fileName,$info['tmp_name']);
            $url = $this->picurl.parse_url($result['info']['url'])['path'];
            $pic = new File();
            $id = $pic->addNew($name.$this->ext,$url,$this->type_id,$info['size'],self::$uid);
            $out = [
                '_id'=>$id,
                '_path'=>$url,
                '_name'=>$name
            ];
            return $this->sendSuccess($out);
        } catch (OssException $e) {
            return $this->sendError(['status'=>1,'path'=>$e->getMessage()]);
        }
    }

    /**
     * @param string $file 名称
     * @param int $type 文件类型
     * @param int $type1 上传类型 1图片,2文件
     * @return array
     */
    protected function toUpload($file='file',$type=1,$type1=1){
        if($type1==1){
            $type_detail = (new PicType())->getById($type);
            $this->bucketName = $type_detail['bucket_name'];
            $this->picurl = $type_detail['picurl'];
            $this->finder = $type_detail['finder_name'];
            $this->ext = $type_detail['ext'];
            $this->type_id = $type;
        }else{
            $type_detail = (new FileType())->getById($type);
            $this->finder = $type_detail['finder_name'];
            $this->picurl = $type_detail['picurl'];
            $this->ext = $type_detail['ext'];
            $this->type_id = $type;
        }
        try {
            $_file = request()->file($file);
            $info = $_file->getInfo();
            $image = Image::open($info['tmp_name']);
            $w = $image->width();
            $h = $image->height();
            $name = $this->setPicName();
            if(empty($this->ext)){
                $_name = explode('.',$info['tmp_name']);
                $this->ext = end($_name);
            }
            $fileName = $this->finder.'/'.$name.$this->ext;
            $result = $this->client->uploadFile($this->bucketName,$fileName,$info['tmp_name']);
            $url = $this->picurl.parse_url($result['info']['url'])['path'];
            if($type==1){
                $_path = "{$url}@!{$this->finder}";
                $pic = new Pic();
                $id = $pic->addNew($name.$this->ext,$_path,$this->type_id,$w,$h,$info['size'],'');
            }else{
                $_path = "{$url}";
                $file = new File();
                $id = $file->addNew($name.$this->ext,$_path,$this->type_id,'','',$info['size'],'');
            }
            $out = [
                'status'=>1,
                '_id'=>$id,
                '_path'=>$_path,
                '_name'=>$name
            ];
            return $out;
        } catch (OssException $e) {
            return ['status'=>0,'path'=>$e->getMessage()];
        }
    }

    /**
     * @title 获取上传token
     * @method access_token
     * @param int $finder 类型 true 1 1图片,2文件
     * @param int $type 类型 true 1 1同上传图片,2同上传文件
     * @param float|int $expire 过期时间 false 300
     * @route('v1/aliyun/access_token','get')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\Xml
     */
    public function access_token($finder=1,$type=1,$expire= 60 * 5){
        if($finder==1){
            $type_detail = (new PicType())->getById($type);
            $this->finder = $dir = $type_detail['finder_name'];
        }else{
            $type_detail = (new FileType())->getById($type);
            $this->finder = $dir = $type_detail['finder_name'];
        }
        if(stripos($dir,'%Ym%')>0){
            $dir = str_replace('%Ym%','/'.date('Ym').'/', $dir);
        }

        $id= $this->appid;
        $key= $this->secret;
        $host = Env::get('ALIYUN.ALIYUN_HOST');
        $callbackUrl = Env::get('ALIYUN.CALLBACK_URL');
        $callback_param = array(
            'callbackUrl'=>$callbackUrl,
            'callbackBody'=>'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}&type_id='.$this->type_id,
            'callbackBodyType'=>"application/x-www-form-urlencoded"
        );
        $callback_string = \GuzzleHttp\json_encode($callback_param);

        $base64_callback_body = base64_encode($callback_string);
        // preg_match_all('/\d+/',$expire,$arr);
        $now = time();
        $end = $now + $expire;
        $expiration = $this->gmt_iso8601($end);
        $conditions = [
//            ['content-length-range', 0, 1024 * 1024 * 5],
            ['starts-with', '$key', $dir]
        ];
        $arr = array('expiration'=>$expiration,'conditions'=>$conditions);
        $policy = \GuzzleHttp\json_encode($arr);
        $base64_policy = base64_encode($policy);
        $string_to_sign = $base64_policy;
        $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true));
        $response = array();
        $name = date('YmdHis',$now);
        $response['accessid'] = $id;
        $response['host'] = $host;
        $response['policy'] = $base64_policy;
        $response['signature'] = $signature;
        $response['expire'] = $end;
        $response['callback'] = $base64_callback_body;
        $response['dir'] = $dir.$name.'-${filename}';
        return $this->sendSuccess($response);
    }

    /**
     * @title 删除文件
     * @method delete
     * @param string $del_obj 删除对象id/地址 true
     * @param string $type 类型 false pic pic图片file文件
     * @route('v1/aliyun/delete','get')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\Xml
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function delete($del_obj='',$type='pic'){
        $type = $type?:$this->file_type;
        if($type=='pic'){
            $obj = (new Pic())->where('id|picurl','eq',$del_obj)->find();
            if($obj){
                $_obj = parse_url($obj['picurl'])['path'];
                if(strpos($_obj,'@!') !== false){
                    $tmp = explode('@!',$_obj);
                    $_obj = substr($tmp[0],1);
                }else{
                    $_obj = substr($_obj,1);
                }
                try {
                    $this->client->deleteObject($this->bucketName,$_obj);
                    $obj->delete();
                    $json = ['status'=>1,'msg'=>'删除成功','data'=>$obj['picurl']];
                }catch (Exception $ex){
                    $json = ['status'=>0,'msg'=>$ex,'data'=>$_obj];
                }

            }else{
                $json = ['status'=>0,'msg'=>'图片不存在','data'=>''];
            }
        }else{
            $obj = (new File())->where('id|picurl','eq',$del_obj)->find();
            if($obj){
                $_obj = parse_url($obj['picurl'])['path'];
                if (strpos($_obj,'@!') !== false) {
                    $tmp = explode('@!',$_obj);
                    $_obj = substr($tmp[0],1);
                }else{
                    $_obj = substr($_obj,1);
                }
                try {
                    $this->client->deleteObject($this->bucketName,$_obj);
                    $obj->delete();
                    $json = ['status'=>1,'msg'=>'删除成功','data'=>$obj['picurl']];
                }catch (Exception $ex){
                    $json = ['status'=>0,'msg'=>$ex,'data'=>''];
                }
            }else{
                $json = ['status'=>0,'msg'=>'文件不存在','data'=>''];
            }
        }
        return $this->sendSuccess($json);
    }

    /**
     * @title 删除文件
     * @method delete_all
     * @param array $del_obj 删除对象id/地址 true
     * @param string $type 类型 false pic pic图片file文件
     * @route('v1/aliyun/delete_all','post')
     * @return \think\facade\Response|\think\response\Json|\think\response\Jsonp|\think\response\Redirect|\think\response\Xml
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function delete_all($del_obj=[],$type='pic'){
        $type = $type?:$this->file_type;
        $_result = [];
        if($type=='pic'){
            $obj = (new Pic())->where('id|picurl','in',$del_obj)->select();
            $object = [];
            foreach ($obj as $v){
                $_obj = parse_url($v['picurl'])['path'];
                if(strpos($_obj,'@!') !== false){
                    $tmp = explode('@!',$_obj);
                    $_obj = substr($tmp[0],1);
                }else{
                    $_obj = substr($_obj,1);
                }
                array_push($object,$_obj);
            }
            try {
                $this->client->deleteObjectS($this->bucketName,$object);
                (new Pic())->where('id|picurl','in',$del_obj)->delete();
                $json = ['status'=>1,'msg'=>'删除成功','data'=>$obj['picurl']];
            }catch (Exception $ex){
                $json = ['status'=>0,'msg'=>$ex,'data'=>$_obj];
            }
        }else{
            $obj = (new File())->where('id|picurl','in',$del_obj)->select();
            $object = [];
            foreach ($obj as $v){
                $_obj = parse_url($v['picurl'])['path'];
                if(strpos($_obj,'@!') !== false){
                    $tmp = explode('@!',$_obj);
                    $_obj = substr($tmp[0],1);
                }else{
                    $_obj = substr($_obj,1);
                }
                array_push($object,$_obj);
            }
            try {
                $this->client->deleteObjectS($this->bucketName,$object);
                (new Pic())->where('id|picurl','in',$del_obj)->delete();
                $json = ['status'=>1,'msg'=>'删除成功','data'=>$obj['picurl']];
            }catch (Exception $ex){
                $json = ['status'=>0,'msg'=>$ex,'data'=>''];
                array_push($_result,$json);
            }
        }
        return $this->sendSuccess($json);
    }

    /**
     * @title ueditor上传
     * @method upload_editor
     * @param string $filename 文件字段 true upfile
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function upload_editor($filename="upfile"){
        $action = $_GET['action'];
        $arr = ['state'=> '没有上传文件'];
        if($_FILES){
            $_file = $_FILES[$filename];
            $tmp = explode('.',$_file['name']);
            $ext = end($tmp);
            $fileName = $_file['name'];
            $arr = array(
                'state'=>'SUCCESS',
                'original'=>$fileName,
                'type'=>$ext,
                'size'=>$_file['size']
            );
        }
        switch ($action){
            case 'listimage':
                $p = input('start',1,'intval');
                $limit = input('size',20,'intval');
                $pic = new Pic();
                $map = ['type'=>6];
                $list = $pic->getList($map,$p,$limit);
                $data = [];
                $total = $pic->count('*');
                foreach($list as $k => $v){
                    $data[$k]['url'] = $v['picurl'];
                    $data[$k]['mtime'] = strtotime($v['create_time']);
                }
                $arr = array(
                    'state'=>'SUCCESS',
                    'list'=>$data,
                    'start'=>$p,
                    'total'=>$total
                );
                $result = \GuzzleHttp\json_encode($arr);
                break;
            case 'listfile':
                $p = input('start',1,'intval');
                $limit = input('size',20,'intval');
                $file = new File();
                $list = $file->getList($p,$limit);
                $total = $file->count('*');
                $data = [];
                foreach($list as $k => $v){
                    $data[$k]['url'] = $v['picurl'];
                    $data[$k]['mtime'] = strtotime($v['create_time']);
                }
                $arr = array(
                    'state'=>'SUCCESS',
                    'list'=>$data,
                    'start'=>$p,
                    'total'=>$total
                );
                $result = \GuzzleHttp\json_encode($arr);
                break;
            case 'uploadimage':
                $_result = $this->toUpload($filename,5,1);
                if($_result['status']){
                    $arr['url']=$_result['_path'];
                    $arr['title']=$_result['_name'];
                }
                $result = \GuzzleHttp\json_encode($arr);
                break;
            case 'uploadvideo':
                $_result = $this->toUpload($filename,6,2);
                if($_result['status']){
                    $arr['url']=$_result['data']['path'];
                    $arr['title']=$_result['_name'];
                }
                $result = \GuzzleHttp\json_encode($arr);
                break;
            case 'config':
            default:
                $config = \GuzzleHttp\json_decode(preg_replace("/\/\*[\s\S]+?\*\//", "",
                    file_get_contents("./static/config.json")), true);
                $result =  \GuzzleHttp\json_encode($config);
                break;
        }
        /* 输出结果 */
        if (isset($_GET["callback"])) {
            if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
                echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
            } else {
                echo json(['state'=> 'callback参数不合法']);
            }
        } else {
            echo $result;
        }
    }

    /**
     * @param string $name
     * @return string
     */
    private function setPicName(&$name=''){
        $typeData = $this->getTypeFinder();
        if(is_array($typeData)){
            $typeData = implode('/', $typeData);
        }
        $name =  'jie-'.date('YmdHis',time());
        $micr = explode(".", microtime(true));
        $name .= rand(0, 9).$micr[1];
        return $typeData . DIRECTORY_SEPARATOR . $name;
    }

    /**
     * @return false|mixed|string[]
     */
    private function getTypeFinder(){
        $typeData = $this->finder;
        if(stripos($typeData,'%Ym%')>0){
            $this->finder = str_replace('%Ym%','', $typeData);
            $typeDatas = str_replace('%Ym%','/'.date('Ym'), $typeData);
            $typeData = explode('/', $typeDatas);
        }
        return $typeData;
    }

    /**
     * @param $time
     * @return string
     * @throws \Exception
     */
    private function gmt_iso8601($time) {
        $dtStr = date("c", $time);
        $mydatetime = new \DateTime($dtStr);
        $expiration = $mydatetime->format(\DateTime::ISO8601);
        $pos = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration."Z";
    }
}